package com.jl15988.chainlog.aspect;

import com.jl15988.chainlog.annotation.ChainApi;
import com.jl15988.chainlog.annotation.ChainController;
import com.jl15988.chainlog.annotation.ChainCustom;
import com.jl15988.chainlog.annotation.ChainDisabled;
import com.jl15988.chainlog.callback.ChainApiCallback;
import com.jl15988.chainlog.config.ChainLogProperties;
import com.jl15988.chainlog.constant.ChainLogConstant;
import com.jl15988.chainlog.context.ChainLogContext;
import com.jl15988.chainlog.model.ChainApiLog;
import com.jl15988.chainlog.util.AspectUtil;
import com.jl15988.chainlog.util.ChainLogUtil;
import com.jl15988.chainlog.util.ExceptionUtil;
import com.jl15988.chainlog.util.IdUtil;
import com.jl15988.chainlog.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * 接口监控切面
 */
@Aspect
@Order(1)
@Slf4j
public class ChainApiAspect {

    @Autowired
    private ChainLogProperties chainLogProperties;

    @Autowired(required = false)
    private List<ChainApiCallback> callbacks;

    /**
     * 全局监控切点 - 所有Controller方法
     */
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController) || " +
            "@within(org.springframework.stereotype.Controller)")
    public void globalControllerPointcut() {
    }

    /**
     * 注解监控切点 - ChainApi注解
     */
    @Pointcut("@annotation(com.jl15988.chainlog.annotation.ChainApi)")
    public void annotationPointcut() {
    }

    /**
     * Controller级别监控切点 - ChainController注解
     */
    @Pointcut("@within(com.jl15988.chainlog.annotation.ChainController)")
    public void controllerAnnotationPointcut() {
    }

    /**
     * 环绕通知
     */
    @Around("(globalControllerPointcut() && execution(public * *(..))) || annotationPointcut() || controllerAnnotationPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Date startTime = new Date();
        // 检查是否启用监控
        if (!chainLogProperties.isEnabled()) {
            return joinPoint.proceed();
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = joinPoint.getTarget().getClass();

        // 检查是否有NoMonitor注解
        if (method.isAnnotationPresent(ChainDisabled.class) || targetClass.isAnnotationPresent(ChainDisabled.class)) {
            return joinPoint.proceed();
        }

        // 检查Controller级别注解
        ChainController chainController = targetClass.getAnnotation(ChainController.class);

        // 检查是否是可视化API，如果配置不监控可视化API则跳过
        if (chainController != null && !chainLogProperties.getVisualization().isIncludeItself()) {
            if (Arrays.stream(chainController.customs()).anyMatch(chainCustoms -> ChainLogConstant.ChainTraceControllerKey.equals(chainCustoms.key()))) {
                return joinPoint.proceed();
            }
        }

        if (chainController != null && !chainController.enabled()) {
            return joinPoint.proceed();
        }

        // 检查监控模式
        ChainApi chainApi = method.getAnnotation(ChainApi.class);
        if (chainApi == null) {
            chainApi = targetClass.getAnnotation(ChainApi.class);
        }

        if (chainLogProperties.getMode() == ChainLogProperties.MonitorMode.ANNOTATION &&
                chainApi == null && chainController == null) {
            // 注解模式下没有注解则不再继续监控
            return joinPoint.proceed();
        }

        // 检查排除规则
        String className = targetClass.getName();
        String methodName = method.getName();
        if (ChainLogUtil.shouldExclude(className, methodName,
                chainLogProperties.getExcludePackages(),
                chainLogProperties.getExcludeClasses(),
                chainLogProperties.getExcludeMethods())) {
            return joinPoint.proceed();
        }

        // 获取HTTP请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return joinPoint.proceed();
        }

        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();

        // 创建监控信息
        ChainApiLog chainApiLog = createChainApiLog(request, method, targetClass, chainApi, chainController, startTime, joinPoint);
        ChainLogContext.setApiMonitorInfo(chainApiLog);

        Object result = null;
        Throwable exception = null;

        try {
            // 执行目标方法
            result = joinPoint.proceed();

            // 记录响应参数
            chainApiLog.setResult(result);
            chainApiLog.setSuccess(true);
        } catch (Throwable e) {
            exception = e;
            chainApiLog.setSuccess(false);

            // 记录异常信息
            chainApiLog.setExceptionInfo(ExceptionUtil.getExceptionStackTrace(e));
            throw e;
        } finally {
            // 完成监控信息
            completeChainLog(chainApiLog);
            // 执行回调
            executeCallbacks(chainApiLog, request, response, exception);
            // 清理上下文
            ChainLogContext.clearChainApiLog();
        }

        return result;
    }

    /**
     * 创建接口监控信息
     */
    private ChainApiLog createChainApiLog(HttpServletRequest request, Method method,
                                          Class<?> targetClass, ChainApi chainApi, ChainController chainController,
                                          Date startTime, ProceedingJoinPoint joinPoint) {
        ChainApiLog chainApiLog = new ChainApiLog(IdUtil.generateId(), startTime);

        // 基本信息
        chainApiLog.setMethod(targetClass.getName() + "." + method.getName());
        chainApiLog.setClassName(targetClass.getName());
        chainApiLog.setUrl(request.getRequestURI());
        chainApiLog.setHttpMethod(request.getMethod());
        chainApiLog.setClientIp(RequestUtil.getClientIp(request));
        chainApiLog.setUserAgent(request.getHeader("User-Agent"));
        chainApiLog.setHeaders(RequestUtil.getHeaders(request));

        // 构建参数
        Map<String, Object> params = AspectUtil.getParams(joinPoint);
        chainApiLog.setParams(params);

        // 构建自定义参数
        Map<String, String> customParams = new HashMap<>();

        // 先处理Controller级别的配置
        if (chainController != null) {
            chainApiLog.setModule(chainController.module());
            chainApiLog.setOperationType(chainController.operationType());
            chainApiLog.setDesc(chainController.desc());

            // 添加Controller级别的自定义参数
            ChainCustom[] controllerCustoms = chainController.customs();
            if (controllerCustoms != null) {
                for (ChainCustom chainCustom : controllerCustoms) {
                    customParams.put(chainCustom.key(), chainCustom.value());
                }
            }
        }

        // 再处理方法级别的注解信息（会覆盖Controller级别的配置）
        if (chainApi != null) {
            // 方法级别的name优先
            chainApiLog.setOperationName(chainApi.name().isEmpty() ? method.getName() : chainApi.name());

            // 如果方法级别有module，则覆盖Controller级别的module
            if (!chainApi.module().isEmpty()) {
                chainApiLog.setModule(chainApi.module());
            }

            // 如果方法级别有operationType，则覆盖Controller级别的operationType
            if (!chainApi.operationType().isEmpty()) {
                chainApiLog.setOperationType(chainApi.operationType());
            }

            // 如果方法级别有desc，则覆盖Controller级别的desc
            if (!chainApi.desc().isEmpty()) {
                chainApiLog.setDesc(chainApi.desc());
            }

            // 添加方法级别的自定义参数（会覆盖同名的Controller级别参数）
            ChainCustom[] methodCustoms = chainApi.customs();
            if (methodCustoms != null) {
                for (ChainCustom chainCustom : methodCustoms) {
                    customParams.put(chainCustom.key(), chainCustom.value());
                }
            }
        } else if (chainController != null) {
            // 如果没有方法级别注解，使用方法名作为操作名称
            chainApiLog.setOperationName(method.getName());
        }

        chainApiLog.setCustoms(customParams);

        return chainApiLog;
    }

    /**
     * 完成监控信息
     */
    private void completeChainLog(ChainApiLog chainApiLog) {
        Date endTime = new Date();
        chainApiLog.setEndTime(endTime);
        chainApiLog.setDuration(endTime.getTime() - chainApiLog.getStartTime().getTime());

        // 截断过长的参数
//        if (chainApiLog.getParams() != null) {
//            String requestParamsStr = MonitorUtils.toJsonString(chainApiLog.getParams());
//            requestParamsStr = MonitorUtils.truncateString(requestParamsStr, monitorProperties.getMaxParamLength());
//            chainApiLog.setParams(requestParamsStr);
//        }

//        if (chainApiLog.getResponseParams() != null) {
//            String responseParamsStr = MonitorUtils.toJsonString(chainApiLog.getResponseParams());
//            responseParamsStr = MonitorUtils.truncateString(responseParamsStr, monitorProperties.getMaxParamLength());
//            monitorInfo.setResponseParams(responseParamsStr);
//        }
    }

    /**
     * 执行回调
     */
    private void executeCallbacks(ChainApiLog monitorInfo, HttpServletRequest request,
                                  HttpServletResponse response, Throwable exception) {
        if (callbacks == null || callbacks.isEmpty()) {
            return;
        }

        // 异步执行回调
        CompletableFuture.runAsync(() -> {
            for (ChainApiCallback callback : callbacks) {
                try {
                    if (exception != null) {
                        callback.onChainException(monitorInfo, request, response, exception);
                    } else {
                        callback.onChainComplete(monitorInfo, request, response);
                    }
                } catch (Exception e) {
                    // 记录回调异常，但不影响主流程
                    log.warn(e.getMessage(), e);
                }
            }
        });
    }
}